
import { _decorator, Component, Node, Graphics, Color, Enum, Vec2, v2, CCInteger, Size } from 'cc';
import { App } from '../../App';

enum GraphicsType{
    NONE=0, 
    QRCODE,
    LINE,
    WAVE,
};

enum WaveType{
    SINE=0,
    SIGN,
    SQUARE,
    SAWTOOTH,
    TRIANGLE
};
enum EaseType{
    LINEAR=0,
    SINEIN,
    SINEOUT,
    SINEINOUT,
};
interface Property{
    lineWidth:number,            // 线条宽度
    strokeColor:Color,           // 线段颜色
    fillColor:Color,             // 填充颜色
    miterLimit:number,           // 设置斜接面限制比例
};

const PI180 = Math.PI / 180;
const PI2 = Math.PI*2;
const HALFPI = Math.PI*0.5;

class Wave{
    private _eCall:Array<(p:number,a:number)=>number> = null;
    private _wCall:Array<(x:number)=>number>= null;
    constructor(){
        this._eCall = [this._linear,this._sinein,this._sineout,this._sineinout];
        this._wCall = [this._sine,this._sign,this._square,this._sawtooth,this._triangle];
    }

    public easeCall(ease:EaseType,p:number,a:number){
        const call:(p:number,a:number)=>number = this._eCall[ease];
        return call && call(p,a);
    }

    public waveCall(wave:WaveType,x:number){
        const call:(a:number)=>number = this._wCall[wave];
        return call && call(x);
    }
    
    private _linear(percent:number, amplitude:number) {return amplitude;}
    private _sinein(percent:number, amplitude:number) {return amplitude * (Math.sin(percent * Math.PI - HALFPI) + 1) * 0.5;}
    private _sineout(percent:number, amplitude:number) {return amplitude * (Math.sin(percent * Math.PI + HALFPI) + 1) * 0.5;}
    private _sineinout(percent:number, amplitude:number){return amplitude * (Math.sin(percent * PI2 - HALFPI) + 1) * 0.5;}

    private _sine(x:number) {return Math.sin(x);};
    private _sign(x:number) {
        x = +x; // convert to a number
        return (x === 0 || isNaN(x)) ? x :(x > 0 ? 1 : -1);
    }
    
    private _square(x:number) {return this._sign(Math.sin(x * PI2));};
    private _sawtooth(x:number) {return (x - Math.floor(x + 0.5)) * 2;};
    private _triangle(x:number) {return Math.abs(this._sawtooth(x));};
}

const { ccclass, property } = _decorator;
 
@ccclass('Primitives')
export class Primitives extends Graphics {
    @property({type:Enum(GraphicsType),displayName:'图元类型',tooltip:"选择类型"})
    select:GraphicsType = GraphicsType.NONE;
    @property({displayName: '二维码文本',visible(){return this.select == GraphicsType.QRCODE}})
    QRString: string = '';
    set QRS(s){this.QRString = s;this.drawQR(s);}
    @property({type:[Vec2],tooltip: '请输入起点和终点',visible(){return this.select == GraphicsType.LINE}})
    twoPoints: Vec2[] = [v2(0,0),v2(0,0)];
    @property({type: CCInteger, displayName: '波纹个数',visible(){return this.select == GraphicsType.WAVE}})
    speed:number = 0;
    @property({type: Enum(WaveType), displayName: '波纹类型',visible(){return this.select == GraphicsType.WAVE}})
    waveType: WaveType = WaveType.SINE;
    @property({type: Enum(EaseType), displayName: '平滑类型',visible(){return this.select == GraphicsType.WAVE}})
    easeType: EaseType = EaseType.LINEAR;
    @property({type: CCInteger, displayName: '刷新频率',visible(){return this.select == GraphicsType.WAVE}})
    timeModifier:number = 1;
    @property({type: CCInteger, displayName: '振幅',visible(){return this.select == GraphicsType.WAVE}})
    amplitude:number = 50;
    @property({type: CCInteger, displayName: '波长',visible(){return this.select == GraphicsType.WAVE}})
    wavelength:number = 50;
    @property({type: CCInteger, displayName: '分段长度',visible(){return this.select == GraphicsType.WAVE}})
    segmentLength:number = 10;

    private _wave:Wave = null;
    private _time:number = 0;
    private _yAxis = 0;
    private _width = 0;
    private _waveWidth = 0;
    private _waveLeft = 0;

    onLoad () {
        if (this.select === GraphicsType.WAVE){
            const nodeSize:Size = App.contentSize(this);
            this._yAxis = nodeSize.height/2;
            this._width = nodeSize.width;
            this._waveWidth = nodeSize.width * 0.95;
            this._waveLeft = nodeSize.width * 0.25;
            this._wave = new Wave();
        }
    }

    /**
     * 绘制线段
     * @param start 起点 
     * @param end  终点
     * @param attribute 属性 
     */
    public drawLine(start:Vec2,end:Vec2,isClear:boolean = true,attribute?:Property){
        //if (this.select !== GraphicsType.LINE) return ;
        this._setAttribute(attribute);
        isClear && this.clear();
        this.moveTo(start.x,start.y);//起始点
        this.lineTo(end.x,end.y);//终点
        this.stroke();//填充路径
    }

    /**
     * 绘制二阶贝塞尔曲线
     * @param start 
     * @param end 
     * @param control 
     * @param attribute 
     */
    public drawBezier(start:Vec2,end:Vec2,control:Vec2,isClear:boolean = true,attribute?:Property) {
        //if (this.select !== GraphicsType.LINE) return ;
        this._setAttribute(attribute);
        isClear && this.clear();
        this.moveTo(start.x, start.y);
        this.quadraticCurveTo(control.x, control.y, end.x, end.y);
        this.stroke();
    }

    public drawQR(text:string,fileColor:Color = Color.BLACK,isClear:boolean=true){
        if (this.select !== GraphicsType.QRCODE) return ;
        text = App.Util.trim(text);
        if (App.Util.isEmpty(text)) return ;
        let nodesize:Size = App.contentSize(this);
        if (nodesize.width < 128 || Math.floor(nodesize.width%2) !== 0 || nodesize.height < 128 || Math.floor(nodesize.height%2) !== 0){
            nodesize = App.contentSize(this,128,128);
        }
        isClear && this.clear();
        // @ts-ignore
        const qrcode = new QRCode(-1, QRErrorCorrectLevel.H);
        if (!qrcode){
            console.error('Please install QRCode library.');
            return ;
        }
        qrcode.addData(text);
        qrcode.make();

        this.fillColor = fileColor; // 二维码颜色
        // compute tileW/tileH based on node width and height
        let tileW = nodesize.width / qrcode.getModuleCount();
        let tileH = nodesize.height / qrcode.getModuleCount();

        // draw in the Graphics
        for (let row = 0; row < qrcode.getModuleCount(); row++) {
            for (let col = 0; col < qrcode.getModuleCount(); col++) {
                if (qrcode.isDark(row, col)) {
                    let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
                    let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
                    this.rect(Math.round(col * tileW), Math.round(row * tileH), w, h);
                    this.fill();
                }
            }
        }
    }

    protected update(dt: number): void {
        if (!this._wave) return ;

        this.clear();
        this._time = dt;
        this._drawWave(this._time*this.timeModifier);
    }

    private _drawWave(time: number){
        // Starting Line
        this.moveTo(0, this._yAxis);
        this.lineTo(this._waveLeft, this._yAxis);

        for (let i = 0; i < this._waveWidth; i += this.segmentLength) {
            // Calculate where the next point is
            let point = this._getPoint(time, i);
            // Draw to it
            this.lineTo(point.x, point.y);
        }
        this.lineTo(this._width, this._yAxis);
        this.stroke();
    }

    private _setAttribute(cfg:Property){
        this.lineWidth = cfg.lineWidth;
        this.strokeColor = cfg.strokeColor;
        this.fillColor = cfg.fillColor;
        this.miterLimit = cfg.miterLimit;
    }

    private _getPoint(time:number, position:number):Vec2{
        let x = (time * this.speed) + (-this._yAxis + position);
        let y = this._wave.waveCall(this.waveType,x);

        // Left and Right Sine Easing
        let amplitude = this._wave.easeCall(this.easeType,position / this._waveWidth, this.amplitude);
        x = position + this._waveLeft;
        y = amplitude * y + this._yAxis;

        return v2(x,y);
    }
}
